#include <iostream>
#include <string>
#include <new>
#include <libpq-fe.h>
#include <glib.h>
#include "horarios.h"
#include "docentes.h"
#include "turmas.h"
#include "carrega.h"

using namespace std;

typedef struct{
  gchar *host;
  gchar *name;
  gchar *user;
  gchar *pass;
} DBconf;

PGconn *DB = NULL;  //Variavel Global de conexao com o bando de dados

/*
* Abre a conexao DB com o bando de dados SWACD
* @return int 1 se a conexão foi aberta, 0 caso contrario
*/
int openDB(){

	char *stringConect;
	DBconf *conf;
	gchar *name;
	GKeyFile *keyfile;
	GKeyFileFlags flags;
	GError *error = NULL;
	gsize length;

	// Create a new GKeyFile object and a bitwise list of flags.
	keyfile = g_key_file_new ();
	flags = G_KEY_FILE_KEEP_COMMENTS;

	// Load the GKeyFile from keyfile.conf or return.
	if (!g_key_file_load_from_file (keyfile, "swacd.conf", flags, &error)){
		g_error (error->message);
		return -1;
	}

	// Create a new Settings object. If you are using GTK+ 2.8 or below, you should
	// use g_new() or g_malloc() instead!
	conf = g_slice_new (DBconf);

	// Read in data from the key file from the group "swacd-db".
	conf->name = g_key_file_get_string (keyfile, "swacd-db","Name", NULL);
	conf->user = g_key_file_get_string (keyfile, "swacd-db","User", NULL);
	conf->pass = g_key_file_get_string (keyfile, "swacd-db","Pass", NULL);

	stringConect = g_strdup_printf("dbname=%s user=%s password=%s",conf->name,conf->user,conf->pass);
	DB = PQconnectdb(stringConect);
	//DB = PQconnectdb("dbname=swacd user=swacd password=\'swacd\'");
	if(PQstatus(DB) == CONNECTION_BAD){
		cout << "Error: " << PQerrorMessage(DB) << endl;
		PQfinish(DB);
		return 0;
	}
	cout << "Conexão efetuada com sucesso." << endl;
	return 1;
}

/*
* Fecha a conexao DB com o bando de dados SWACD
* @return int 1 conexão fechada
*/
int closeDB(){
	PQfinish(DB);
	return 1;
}

int removePrefConflit(ArrayDocentes *docentes, ArrayTurmas *turmas){
	// Dados de Horario esta errado TODO Fazer static
	int numDias= (*docentes).docentes[0].restricao.getNumDias();
	int numHoras= (*docentes).docentes[0].restricao.getNumHoras();
	int p, t, d, h, conflita;

	// Penaliza os conflitos com disciplinas de acordo com as restricões
	for(p=0; p<(*docentes).numDocentes; p++){
		for(t=0; t<(*turmas).numTurmas; t++){
			conflita=0;
			for(d=0;d<numDias;d++)
				for(h=0;h<numHoras;h++)
					if((*turmas).turmas[t].grade.getValor(d,h) && (*docentes).docentes[p].restricao.getValor(d,h)){
						conflita=1;
						d=numDias;
						h=numHoras;
					}
			if(conflita)
				(*docentes).docentes[p].pref[t]= -1;
		}
	}
}

int getPrefDocentes(ArrayDocentes *docentes, ArrayTurmas *turmas){
	PGresult *result;    // Resultado de uma query, para verificar use PGRES_COMMAND_OK ou PGRES_TUPLES_OK:
	string nusp, disc;   // Professor da restricao e disciplina preferida
	int valor;           // Valor da preferencia
	int t;               // indice de turmas

	PQexec(DB,"BEGIN WORK");
	PQexec(DB,"DECLARE curr CURSOR FOR SELECT s.doc, s.disc, s.pref FROM SELECAO s ORDER BY s.doc, s.disc;");
	result= PQexec(DB,"FETCH 1 IN curr");
	while(PQntuples(result) > 0){
		// Dados do BD
		nusp= PQgetvalue(result, 0, 0);
		disc= PQgetvalue(result, 0, 1);
		valor= atoi(PQgetvalue(result, 0, 2));

		// Para cada turma da disciplina disc adiciona as preferencias
		for(t=0; t<(*turmas).numTurmas; t++){
			if((*turmas).turmas[t].disc==disc){
				(*docentes).get(nusp).pref[t]= valor;
			}
		}

		result= PQexec(DB,"FETCH NEXT IN curr");
	}
	PQexec(DB,"COMMIT WORK");
}

int getHoraDocentes(ArrayDocentes *docentes){
	PGresult *result;     // Resultado de uma query, para verificar use PGRES_COMMAND_OK ou PGRES_TUPLES_OK:
	string nusp;          // Disciplina e Turma
	string dia;           // Dia da restricao
	int hora, min;        // Horario da restricao

	PQexec(DB,"BEGIN WORK");
	PQexec(DB,"DECLARE curr CURSOR FOR SELECT r.doc, r.dia, EXTRACT(HOUR FROM r.hora), EXTRACT(MINUTE FROM r.hora) FROM RESTRICAO r ORDER BY r.doc;");
	result= PQexec(DB,"FETCH 1 IN curr");

	while(PQntuples(result) > 0){
		nusp= PQgetvalue(result, 0, 0);
		dia= PQgetvalue(result, 0, 1);
		hora= atoi(PQgetvalue(result, 0, 2));
		min= atoi(PQgetvalue(result, 0, 3));

		(*docentes).get(nusp).restricao.setValor(dia,hora,min,1);

		result= PQexec(DB,"FETCH NEXT IN curr");
	}

	PQexec(DB,"COMMIT WORK");
	return 1;
}

int getDocentes(ArrayDocentes *docentes) {
	PGresult *result;     // Resultado de uma query, para verificar use PGRES_COMMAND_OK ou PGRES_TUPLES_OK:

	// Quantos docentes
	result= PQexec(DB,"SELECT * FROM DOCENTE");
	int numDocentes= PQntuples(result);
	*docentes= ArrayDocentes(numDocentes);

	// Aloca os Arrays de preferencias de acordo com o numero de turmas
	result= PQexec(DB,"SELECT * FROM TURMA");
	int numPref= PQntuples(result);

	// Guarda os professores do bando de dados no Array docentes
	PQexec(DB,"BEGIN WORK");
	PQexec(DB,"DECLARE professores CURSOR FOR SELECT d.nusp, d.ch_pret, EXTRACT(YEAR FROM d.dt_adm), d.regime, d.titulacao, da.area, ca.area FROM DOCENTE d JOIN DOC_EM_AREA da ON d.nusp = da.doc LEFT JOIN COOR_EM_AREA ca ON d.nusp = ca.doc ORDER BY d.nusp;");
	result= PQexec(DB,"FETCH 1 IN professores");
	while(PQntuples(result) > 0){
		 Docente p(
			 PQgetvalue(result, 0, 0)  // nusp
			,PQgetvalue(result, 0, 1)  // ch_pret
			,PQgetvalue(result, 0, 2)  // tempo
			,PQgetvalue(result, 0, 3)  // regime
			,PQgetvalue(result, 0, 4)  // titulacao
			,PQgetvalue(result, 0, 5)  // area
			,PQgetvalue(result, 0, 6)  // coord
			,numPref                   // tamanho do array de preferencia
		);
		(*docentes).add(p);

		result= PQexec(DB,"FETCH NEXT IN professores");
	}
	PQexec(DB,"COMMIT WORK");
}

int getHoraTurmas(ArrayTurmas *turmas){
	PGresult *result;     // Resultado de uma query, para verificar use PGRES_COMMAND_OK ou PGRES_TUPLES_OK:
	string disc, turma;   // Disciplina e Turma
	string dia;           // Dia da restricao
	int hora, min;        // Horario da restricao

	PQexec(DB,"BEGIN WORK");
	PQexec(DB,"DECLARE curr CURSOR FOR SELECT hd.disc, hd.id_turma, hd.dia, EXTRACT(HOUR FROM hd.hora), EXTRACT(MINUTE FROM hd.hora) FROM HOR_DE_DISC hd ORDER BY hd.disc, hd.id_turma;");
	result= PQexec(DB,"FETCH 1 IN curr");

	while(PQntuples(result) > 0){
		disc= PQgetvalue(result, 0, 0);
		turma= PQgetvalue(result, 0, 1);
		dia= PQgetvalue(result, 0, 2);
		hora= atoi(PQgetvalue(result, 0, 3));
		min= atoi(PQgetvalue(result, 0, 4));

		(*turmas).get(disc,turma).grade.setValor(dia,hora,min,1);

		result= PQexec(DB,"FETCH NEXT IN curr");
	}

	PQexec(DB,"COMMIT WORK");
}

int getTurmas(ArrayTurmas *turmas){
	PGresult *result; // Resultado de uma query, para verificar use PGRES_COMMAND_OK ou PGRES_TUPLES_OK:

	// Aloca o Array prof para os professores
	result= PQexec(DB,"SELECT * FROM TURMA");
	int numTurmas= PQntuples(result);
	*turmas = ArrayTurmas(numTurmas);


	// Guarda os professores do bando de dados no Array prof
	PQexec(DB,"BEGIN WORK");
	PQexec(DB,"DECLARE disciplinas CURSOR FOR SELECT t.disc, t.id_turma, t.semestre, da.area, d.ch FROM TURMA t LEFT JOIN DISC_EM_AREA da ON t.disc = da.disc JOIN DISCIPLINA d ON t.disc = d.cod ORDER BY t.disc, t.id_turma;");
	result= PQexec(DB,"FETCH 1 IN disciplinas");
	while(PQntuples(result) > 0){
		 Turma t(
			 PQgetvalue(result, 0, 0)  // disc
			,PQgetvalue(result, 0, 1)  // turma
			,PQgetvalue(result, 0, 2)  // semestre
			,PQgetvalue(result, 0, 3)  // area
			,PQgetvalue(result, 0, 4)  // ch
		);
		(*turmas).add(t);

		result= PQexec(DB,"FETCH NEXT IN disciplinas");
	}
	PQexec(DB,"COMMIT WORK");
}

int loadData(ArrayDocentes *docentes, ArrayTurmas *turmas){
	// Realizando a conexão com a base de dados SWACD
	if(!openDB())
		return 0;

		// Carregando os dados
		getTurmas(turmas);
 		getHoraTurmas(turmas);
   	getDocentes(docentes);
   	getHoraDocentes(docentes);
   	getPrefDocentes(docentes, turmas);

		// Removendo conflitos
		removePrefConflit(docentes, turmas);

	//Fecha o bando de dados e encerra
	closeDB();
	return 1;
}
